package com.siebre.picclife.asynctask;

import static com.google.common.collect.Iterables.getOnlyElement;
import static org.fest.assertions.api.Assertions.assertThat;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.context.annotation.Primary;
import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.testng.AbstractTestNGSpringContextTests;
import org.testng.annotations.Test;

import com.google.common.base.Suppliers;
import com.siebre.picclife.asynctask.PersistenceConfig.DatabaseNameProvider;
import com.siebre.picclife.asynctask.annotation.AsyncTaskProxy;
import com.siebre.picclife.asynctask.annotation.TaskMapping;
import com.siebre.picclife.asynctask.model.Task;
import com.siebre.picclife.asynctask.model.TaskStatus;
import com.siebre.picclife.asynctask.service.TaskRepository;
import com.siebre.picclife.asynctask.springconfig.TaskCreationConfig;
import com.siebre.picclife.asynctask.springconfig.TaskProcessConfig;
import com.siebre.picclife.asynctask.springcontext.annotation.ActivateAsyncTask;

@Test(groups = "demo")
@ContextConfiguration
public class AsyncTaskProcessDemo extends AbstractTestNGSpringContextTests {
	
	@Autowired
	@Qualifier("remoteServiceProxy")
	private RemoteService service;
	
	@Autowired
	private RemoteService actualService;
	
	@Autowired
	private TaskRepository taskRepository;

	public void testCreation() {
		service.newBusiness("TX001");
		
		List<Task> tasks = taskRepository.findByArgument("TX001");
		Task task = getOnlyElement(tasks);
		
		assertThat(task).isNotNull();
		assertThat(task.getStatus()).isEqualTo(TaskStatus.NEW);
		assertThat(task.getType()).isEqualTo("new-business2");
	}
	
	@Test(dependsOnMethods = "testCreation")
	public void testProcess() throws InterruptedException {
		doThrow(RuntimeException.class).when(actualService).newBusiness("TX001");
		Thread.sleep(3000);
		
		List<Task> tasks = taskRepository.findByArgument("TX001");
		Task task = getOnlyElement(tasks);
		
		assertThat(task.getStatus()).isEqualTo(TaskStatus.CORRUPTED);
		
		verify(actualService).newBusiness("TX001");
	}
	
	@AsyncTaskProxy
	public static interface RemoteService {
		
		@TaskMapping(type = "new-business2")
		void newBusiness(String transactionId);
		
		void cancel(String policyNumber);
	}
	
	@Configuration
	@ActivateAsyncTask("com.siebre.picclife.asynctask")
	@Import({TaskCreationConfig.class, TaskProcessConfig.class, PersistenceConfig.class})
	static class Config {
		
		@Bean
		@Primary
		RemoteService remoteService() {
			return mock(RemoteService.class);
		}
		
		@Bean
		static PropertySourcesPlaceholderConfigurer psphConfigurer() {
			return new PropertySourcesPlaceholderConfigurer();
		}
		
		@Bean
		DatabaseNameProvider databaseNameProvider() {
			return new DatabaseNameProvider(Suppliers.ofInstance(AsyncTaskProcessDemo.class.getSimpleName()));
		}
	}
}
